import {
	App,
	Editor,
	MarkdownView,
	Plugin,
	PluginSettingTab,
	Setting,
	moment,
	normalizePath,
	TAbstractFile,
	FileSystemAdapter,
	ListedFiles,
	TFile,
	Notice, TFolder, Vault
} from 'obsidian';
import {
	getAllCachedEmbedsToFile,
	getAllCachedLinksToFile,
	getFileByLink,
	isValidMarkdownNoteInVault,
	splitLinkToPathAndSection
} from "./util";
import * as Path from 'path';

interface CustomAttachmentLocationSettings {
    attachmentFolderPath: string;
    pastedImageFileName: string;
    dateTimeFormat: string;
	attachmentExts: string;
	attachmentSection: string;
	attachmentAlias: string;
    autoRenameFolder: boolean;
    autoRenameFiles: boolean;
	vaultStartLocation: string;
}

const DEFAULT_SETTINGS: CustomAttachmentLocationSettings = {
    attachmentFolderPath: './attachments/${filename}',
    pastedImageFileName: '${filename}-${date}',
    dateTimeFormat: 'YYYYMMDDHHmmss',
	attachmentExts: '.jpeg|.jpg|.png|.gif|.bmp|.tiff|.tif|.svgz|.svg|.jfif|.webp|.ico|.xbm',
	attachmentSection: '#inline',
	attachmentAlias: '300',
    autoRenameFolder: true,
    autoRenameFiles: false,
	vaultStartLocation: ''
}

let originalSettings = {
    attachmentFolderPath: ''
};

const blobToArrayBuffer = (blob: Blob) => {
    return new Promise((resolve) => {
        const reader = new FileReader()
        reader.onloadend = () => resolve(reader.result)
        reader.readAsArrayBuffer(blob)
    })
}


class TemplateString extends String {
    interpolate(params: Object) {
        const names = Object.keys(params);
        const vals = Object.values(params);
        return new Function(...names, `return \`${this}\`;`)(...vals);
    }
}

export default class CustomAttachmentLocation extends Plugin {
    settings: CustomAttachmentLocationSettings;
    useRelativePath: boolean = false;
    adapter: FileSystemAdapter;

    async onload() {
        console.log('loading plugin');

        this.adapter = this.app.vault.adapter as FileSystemAdapter;
        await this.loadSettings();
        this.backupConfigs();
		this.addCommand({
			id: "collect-current-note-attachments",
			name: "Collect current note attachments",
			icon: "plus-square",
			// editorCallback: (editor: Editor, view: MarkdownView) => this.collectCurrentNoteAttachments(editor, view),
			checkCallback: (checking: boolean) => {
				if (checking)
					return !!this.app.workspace.getActiveViewOfType(MarkdownView);
				const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);
				//@ts-ignore
				this.collectCurrentNoteAttachments(mdView.file);
			},
		});
		this.registerEvent(
			this.app.workspace.on("editor-menu", (menu, editor, view) => {
				menu.addItem((item) => {
					item
						.setTitle("Delete attachment")
						.setIcon("trash")
						.onClick(async () => {
							const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);

							if (!mdView) return false;
							if (mdView.editor == undefined) return false;

							const doc = mdView.editor;
							let selectionText = doc.getSelection();
							let text = selectionText;
							if (text.trim() === "") {
								const cursorPosition = doc.getCursor();
								text = doc.getLine(cursorPosition.line);
							}
							const regex: RegExp = new RegExp("!?\\[\\[(.*?)\\]\\]", 'g');
							// const match = regex.exec(text);

							let match;
							let attachmentLinkWithBrackets: string[] = [];
							while ((match = regex.exec(text)) != null) {
								if (match.length >= 2) {
									// const attachmentLinkWithBrackets = match[0];
									attachmentLinkWithBrackets.push(match[0]);
									const attachmentLink = match[1];
									const [attachmentFile, a_, b_] = attachmentLink.split(/#|\|/);
									console.log(attachmentFile);
									//@ts-ignore
									let attachmentFolderPath = this.app.vault.getConfig('attachmentFolderPath'); // e.g. ./attachments/本地游戏库管理工具，Playnite插件LocalGameLibrary

									const activeFile = this.app.workspace.getActiveFile();
									//@ts-ignore
									const attachmentFilePath = Path.join(Path.dirname(activeFile.path), attachmentFolderPath, attachmentFile);
									const file: TAbstractFile | null = this.app.vault.getAbstractFileByPath(normalizePath(attachmentFilePath));
									if (file) {
										const backLinks = getAllCachedLinksToFile(file.path);
										const backEmbeds = getAllCachedEmbedsToFile(file.path);
										if (Object.keys(backLinks).length + Object.keys(backEmbeds).length > 1){
											new Notice(`Failed, There are ${Object.keys(backLinks).length + Object.keys(backEmbeds).length} backlinks.`)
										} else {
											await this.app.vault.delete(file);
										}
									} else {
										new Notice(`attachment not found ${attachmentFilePath}`);
									}
									// this.app.vault.adapter.remove(normalizePath(Path.join(attachmentFolderPath, attachmentFile)));
								}
							}
							// delete the attachment links
							if (selectionText !== "") {
								// const selection = selectionText;
								let newText = selectionText;
								for (const link of attachmentLinkWithBrackets) {
									newText = newText.replace(link, "");
								}
								doc.replaceSelection(newText);
							} else {
								// 替换一行
								let newText2 = text;
								for (const link of attachmentLinkWithBrackets) {
									newText2 = newText2.replace(link, "");
								}
								// let newText = text.replace(attachmentLinkWithBrackets, "");
								doc.setLine(doc.getCursor().line, newText2);
							}
							// if (match != null && match.length >= 2) {
							// 	const attachmentLinkWithBrackets = match[0];
							// 	const attachmentLink = match[1];
							// 	const [attachmentFile, a_, b_] = attachmentLink.split(/#|\|/);
							// 	console.log(attachmentFile);
							// 	//@ts-ignore
							// 	let attachmentFolderPath = this.app.vault.getConfig('attachmentFolderPath'); // e.g. ./attachments/本地游戏库管理工具，Playnite插件LocalGameLibrary
							//
							// 	const activeFile = this.app.workspace.getActiveFile();
							// 	//@ts-ignore
							// 	const attachmentFilePath = Path.join(Path.dirname(activeFile.path), attachmentFolderPath, attachmentFile);
							// 	const file: TAbstractFile | null = this.app.vault.getAbstractFileByPath(normalizePath(attachmentFilePath));
							// 	if (file) {
							// 		const backLinks = getAllCachedLinksToFile(file.path);
							// 		const backEmbeds = getAllCachedEmbedsToFile(file.path);
							// 		if (Object.keys(backLinks).length + Object.keys(backEmbeds).length > 1){
							// 			new Notice(`Failed, There are ${Object.keys(backLinks).length + Object.keys(backEmbeds).length} backlinks.`)
							// 		} else {
							// 			// await this.app.vault.delete(file);
							// 			if (doc.getSelection() !== "") {
							// 				const selection = doc.getSelection();
							// 				const newSelection = selection.replace(attachmentLinkWithBrackets, "");
							// 				doc.replaceSelection(newSelection);
							// 			} else {
							// 				// 替换一行
							// 				let newText = text.replace(attachmentLinkWithBrackets, "");
							// 				doc.setLine(doc.getCursor().line, newText);
							// 			}
							// 		}
							// 	} else {
							// 		new Notice(`attachment not found ${attachmentFilePath}`);
							// 	}
							// 	// this.app.vault.adapter.remove(normalizePath(Path.join(attachmentFolderPath, attachmentFile)));
							// }
						});
				});
			})
		);

		// this.addCommand({
		// 	id: "delete-selected-attachment",
		// 	name: "Delete selected attachment",
		// 	icon: "plus-square",
		// 	// editorCallback: (editor: Editor, view: MarkdownView) => this.collectCurrentNoteAttachments(editor, view),
		// 	checkCallback: (checking: boolean) => {
		// 		if (checking)
		// 			return !!this.app.workspace.getActiveViewOfType(MarkdownView);
		//
		// 	},
		// });
		// this.addCommand({
		// 	id: "collect-vault-notes-attachments",
		// 	name: "Collect vault notes attachments",
		// 	icon: "plus-square",
		// 	// editorCallback: (editor: Editor, view: MarkdownView) => this.collectCurrentNoteAttachments(editor, view),
		// 	checkCallback: (checking: boolean) => {
		// 		if (checking)
		// 			return !!this.app.workspace.getActiveViewOfType(MarkdownView);
		// 		const startLocation = this.settings.vaultStartLocation.trim();
		// 		this.collectVaultNotesAttachments(startLocation);
		// 	},
		// });

        this.addSettingTab(new CustomAttachmentLocationSettingTab(this.app, this));
        /*
            bind this pointer to handlePaste
            this.registerEvent(this.app.workspace.on('editor-paste', this.handlePaste));
        */
        this.registerEvent(this.app.workspace.on('editor-paste', this.handlePaste.bind(this)));
        this.registerEvent(this.app.workspace.on('editor-drop', this.handleDrop.bind(this)));
        this.registerEvent(this.app.workspace.on('file-open', this.handleFileOpen.bind(this)));

        this.registerEvent(this.app.vault.on('rename', this.handleRename.bind(this)));
		this.registerEvent(this.app.vault.on('delete', this.handleDelete.bind(this)));
    }

    onunload() {
        console.log('unloading plugin');
        this.restoreConfigs();
    }

    async loadSettings() {
        this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
        if (this.settings.attachmentFolderPath.startsWith('./'))
            this.useRelativePath = true;
        else
            this.useRelativePath = false;
    }

    async saveSettings() {
        await this.saveData(this.settings);
    }

    backupConfigs() {
        //@ts-ignore
        originalSettings.attachmentFolderPath = this.app.vault.getConfig('attachmentFolderPath');
    }

    restoreConfigs() {
        //@ts-ignore
        this.app.vault.setConfig('attachmentFolderPath', originalSettings.attachmentFolderPath);
    }

    updateAttachmentFolderConfig(path: string) {
        //@ts-ignore
        this.app.vault.setConfig('attachmentFolderPath', path);
    }

    getAttachmentFolderPath(mdFileName: string) {
        let path = new TemplateString(this.settings.attachmentFolderPath).interpolate({
            filename: mdFileName
        });
        return path;
    }

    getAttachmentFolderFullPath(mdFolderPath: string, mdFileName: string) {
        let attachmentFolder = '';

        if (this.useRelativePath)
            attachmentFolder = Path.join(mdFolderPath, this.getAttachmentFolderPath(mdFileName));
        else {
            attachmentFolder = this.getAttachmentFolderPath(mdFileName);
        }
        return normalizePath(attachmentFolder);
    }

    getPastedImageFileName(mdFileName: string) {
        let datetime = moment().format(this.settings.dateTimeFormat);
        let name = new TemplateString(this.settings.pastedImageFileName).interpolate({
            filename: mdFileName,
            date: datetime
        });
        return name;
    }

	async collectCurrentNoteAttachments(noteFile: TFile) {
		// const mdView = this.app.workspace.getActiveViewOfType(MarkdownView);
		// if (!mdView)
		// 	return;

		// let note = mdView.file;
		//@ts-ignore
		let noteName = noteFile.basename;
		//@ts-ignore
		let notePath = noteFile.path

		let mdFolderPath: string = Path.dirname(notePath);
		let newAttachmentFolderPath: string = this.getAttachmentFolderFullPath(mdFolderPath, noteName);


		this.updateAttachmentFolderConfig(this.getAttachmentFolderPath(noteName));
		if (!this.settings.autoRenameFolder) {
			return;
		}

		const allMarkdownFilesInVault = this.app.vault.getMarkdownFiles();
		let embeds = this.app.metadataCache.getCache(notePath)?.embeds;
		embeds = embeds && embeds.filter((embedCache) => allMarkdownFilesInVault.filter(markdownFile => isValidMarkdownNoteInVault(embedCache, markdownFile)).length === 0) || []
		let links = this.app.metadataCache.getCache(notePath)?.links;
		links = links && links.filter((linkCache) => allMarkdownFilesInVault.filter(markdownFile => isValidMarkdownNoteInVault(linkCache, markdownFile)).length === 0) || []
		let linksOrEmbeds = [...links, ...embeds]
		if (!linksOrEmbeds || linksOrEmbeds.length === 0)
			return;

		// JM: 这里倒是应该是除了.md外所有后缀都要考虑，因为不像handleRename那边可以重命名文件夹
		// 这里是必须逐个embed去收集
		for (let linkOrEmbed of linksOrEmbeds) {
			let linkSection = splitLinkToPathAndSection(linkOrEmbed.link);
			if (linkSection.link.trim() === "" && linkSection.hasSection) {
				console.log(`skipped internal link ${linkSection.section}`);
				continue;
			}
			let link = linkSection.link
			let file = getFileByLink(link, notePath) // 获取link所指文件
			if (file) {
				let filePath = file.path;
				let fileName = Path.basename(filePath); // 获取link所指文件的文件名
				// fileName = "ABC" + fileName;
				if (fileName.endsWith(".excalidraw.md") && !fileName.startsWith(noteName)){ // 过滤掉已经重命名过的excalidraw文件
					fileName = this.getPastedImageFileName(noteName) + ".excalidraw.md";
					await this.sleep(1000);
					// fileName = noteName + ".excalidraw.md";
				}
				let newFilePath = normalizePath(Path.join(newAttachmentFolderPath, fileName)); // 构造出link所指文件的新的路径
				// make sure ./attachments folder exists
				let newAttachmentParentFolderPath: string = Path.dirname(newAttachmentFolderPath)
				if (!(await this.adapter.exists(newAttachmentParentFolderPath))) {
					await this.app.vault.createFolder(newAttachmentParentFolderPath);
				}
				// make sure ./attachments/xxx folder exists
				if (!await this.adapter.exists(newAttachmentFolderPath))
					await this.adapter.mkdir(newAttachmentFolderPath);
				await this.app.fileManager.renameFile(file, newFilePath); // 这里其实只是改路径，不是真的改文件名，但是如果真的改了文件名，那么原笔记中的链接也是会自动跟着改名的。
			} else {
				console.log(`link file not found: ${link} in note: ${notePath}`);
				new Notice(`link file not found: ${link} in note: ${notePath}`);
			}
		}
	}

	async sleep(ms: number) {
		return new Promise((resolve) => {
			setTimeout(resolve, ms);
		});
	}

	async collectVaultNotesAttachments(startLocation: string) {
		const files: TFile[] = [];
		// const startLocation = "2_Cards/个人";
		// const startLocation = "0_Workshop/working/obsidian-selection-replace";
		// const startLocation = this.settings.vaultStartLocation.trim();
		const folderOrFile = this.app.vault.getAbstractFileByPath(startLocation);
		if (folderOrFile instanceof TFile) {
			files.push(folderOrFile);
		} else if (folderOrFile instanceof TFolder) {
			Vault.recurseChildren(folderOrFile, (file => {
				if (file instanceof TFile && file.extension === "md") {
					files.push(file);
				}
			}));
		}
		if (files.length <= 0) {
			new Notice(`found ${files.length} files to collect.`);
		}
		files.forEach(markdownFile => {
			this.collectCurrentNoteAttachments(markdownFile);
		});
	}

    async handlePaste(event: ClipboardEvent, editor: Editor, view: MarkdownView) {
        console.log('Handle Paste');

        // @ts-ignore
		let mdFileName = view.file.basename;
        // @ts-ignore
		let mdFolderPath: string = Path.dirname(view.file.path);

        let path = this.getAttachmentFolderPath(mdFileName);
        let fullPath = this.getAttachmentFolderFullPath(mdFolderPath, mdFileName);

        /*
        sample
        this.app.vault.setConfig('attachmentFolderPath', `./assets/${filename}`);
        */
        this.updateAttachmentFolderConfig(path);

        let clipBoardData = event.clipboardData;
        if (clipBoardData == null || clipBoardData.items == null)
            return;
        let clipBoardItems = clipBoardData.items;
        if (!clipBoardData.getData('text/plain')) {
            for (let i in clipBoardItems) {
                if (!clipBoardItems.hasOwnProperty(i))
                    continue;
                let item = clipBoardItems[i];
                if (item.kind !== 'file')
                    continue;
                if (!(item.type === 'image/png' || item.type === 'image/jpeg'))  // text/plain
                    continue;

                let pasteImage = item.getAsFile();
                if (!pasteImage)
                    continue;

                let extension = '';
                item.type === 'image/png' ? extension = 'png' : item.type === 'image/jpeg' && (extension = 'jpeg');

                event.preventDefault();

                //if folder not exist, mkdir first.
                if (!await this.adapter.exists(fullPath))
                    await this.adapter.mkdir(fullPath);

                let img = await blobToArrayBuffer(pasteImage);

                /*
                sample
                let name = 'image-' + moment().format('YYYYMMDDHHmmssSSS');
                */
				// JM: 这里意思是只有图片格式附件才需要重命名
                let name = this.getPastedImageFileName(mdFileName);

				const section = this.settings.attachmentSection;
				const alias = this.settings.attachmentAlias;

                //@ts-ignore
                let imageFile = await this.app.saveAttachment(name, extension, img);
				//@ts-ignore
                let markdownLink = await this.app.fileManager.generateMarkdownLink(imageFile, view.file.path, section, alias);
                markdownLink += '\n\n';
                editor.replaceSelection(markdownLink);
            }
        }
    }

    async handleDrop(event: DragEvent, editor: Editor, view: MarkdownView) {
        console.log('Handle Drop');

        // @ts-ignore
		let mdFileName = view.file.basename;
        // @ts-ignore
		let mdFolderPath: string = Path.dirname(view.file.path);

        let path = this.getAttachmentFolderPath(mdFileName);
        let fullPath = this.getAttachmentFolderFullPath(mdFolderPath, mdFileName);

        if (!this.useRelativePath && !await this.adapter.exists(fullPath))
            await this.app.vault.createFolder(fullPath);

        this.updateAttachmentFolderConfig(path);
    }

    async handleFileOpen(file: TFile | null) {
        console.log('Handle File Open');

        if (file == null) {
            console.log("No file open");
            return;
        }

        if (file.extension !== 'md')
            return;

        let mdFileName = file.basename;

        let path = this.getAttachmentFolderPath(mdFileName);

        this.updateAttachmentFolderConfig(path);
    }

    async handleRename(newFile: TFile, oldFilePath: string) {
        console.log('Handle Rename');

        if (newFile.extension !== 'md')
            return;

        let newName = newFile.basename;

        this.updateAttachmentFolderConfig(this.getAttachmentFolderPath(newName));

        if (!this.settings.autoRenameFolder) {
            return;
        }

        let oldName = Path.basename(oldFilePath, '.md');

        let mdFolderPath: string = Path.dirname(newFile.path);
        let oldMdFolderPath: string = Path.dirname(oldFilePath);
        let oldAttachmentFolderPath: string = this.getAttachmentFolderFullPath(oldMdFolderPath, oldName);
        let newAttachmentFolderPath: string = this.getAttachmentFolderFullPath(mdFolderPath, newName);

        //check if old attachment folder exists and is necessary to rename Folder
        if (await this.adapter.exists(oldAttachmentFolderPath) && (oldAttachmentFolderPath !== newAttachmentFolderPath)) {
            let tfolder = this.app.vault.getAbstractFileByPath(oldAttachmentFolderPath);

            if (tfolder == null)
                return;

            let newAttachmentParentFolderPath: string = Path.dirname(newAttachmentFolderPath)
            if (!(await this.adapter.exists(newAttachmentParentFolderPath))) {
                await this.app.vault.createFolder(newAttachmentParentFolderPath);
            }

            await this.app.fileManager.renameFile(tfolder, newAttachmentFolderPath); // JM: TODO: 这句有问题，会把附件所在文件夹整个重命名，这里的假设应该是每个笔记都必须有独自的文件夹

            let oldAttachmentParentFolderPath: string = Path.dirname(oldAttachmentFolderPath)
            let oldAttachmentParentFolderList: ListedFiles = await this.adapter.list(oldAttachmentParentFolderPath);
            if (oldAttachmentParentFolderList.folders.length === 0 && oldAttachmentParentFolderList.files.length === 0) {
                await this.adapter.rmdir(oldAttachmentParentFolderPath, true);
            }
        }

        //if autoRenameFiles is off
        if (!this.settings.autoRenameFiles)
            return;

        let embeds = this.app.metadataCache.getCache(newFile.path)?.embeds;
        if (!embeds)
            return;

        let files: string[] = [];

        for (let embed of embeds) {
            let link = splitLinkToPathAndSection(embed.link).link;
			// if (link.endsWith('.png') || link.endsWith('jpeg'))  // JM: TODO: 支持更多类型的附件 // TODO: 还有问题就是笔记内引用另一份笔记 // TODO: 或者是excalidraw.md笔记（应该作为和md同等笔记处理比较简单）
			let linkExt = Path.extname(link);
			if (linkExt.trim() === ""){ // JM: embed中引用的是md则默认没有后缀，需要手动添加
				linkExt = ".md"
			}
			if (this.settings.attachmentExts.contains(linkExt))
                files.push(Path.basename(link));
            else
                continue;

        }

        let attachmentFiles: ListedFiles = await this.adapter.list(newAttachmentFolderPath);
        for (let file of attachmentFiles.files) {
            console.log(file);
            let filePath = file;
            let fileName = Path.basename(filePath);
            if ((files.indexOf(fileName) > -1) && fileName.contains(oldName)) { // JM: 附件fileName在files中存在 并且 附件fileName和笔记名开头一致 // JM: 意思是如果附件名称和笔记名称一致的话
                fileName = fileName.replace(oldName, newName);
                let newFilePath = normalizePath(Path.join(newAttachmentFolderPath, fileName));
                let tfile = this.app.vault.getAbstractFileByPath(filePath);
                if (tfile == null)
                    continue;
                await this.app.fileManager.renameFile(tfile, newFilePath);
            }
            else
                continue;
        }
    }

	async handleDelete(deletedFile: TFile) {
		if (deletedFile.extension !== 'md')
			return;

		let fileName = Path.basename(deletedFile.path, '.md');

		let mdFolderPath: string = Path.dirname(deletedFile.path);
		let attachmentFolderPath: string = this.getAttachmentFolderFullPath(mdFolderPath, fileName);

		//check if old attachment folder exists and is necessary to rename Folder
		if (await this.adapter.exists(attachmentFolderPath)) {
			let attachmentFiles: ListedFiles = await this.adapter.list(attachmentFolderPath);
			for (let file of attachmentFiles.files) {
				// 检查所有附件是否被别的笔记引用
				const allLinks = getAllCachedLinksToFile(file);
				const allEmbeds = getAllCachedEmbedsToFile(file);
				console.log(allLinks);
				console.log(allEmbeds);
				if (Object.keys(allEmbeds).length <= 0 && Object.keys(allLinks).length <= 0) {
					// 该附件没有被任何其他笔记引用，可以安全删除
					const trashFolderPath = "TrashAttachments"
					if (!await this.adapter.exists(trashFolderPath))
						await this.adapter.mkdir(trashFolderPath);
					if (!await this.adapter.exists(normalizePath(Path.join(trashFolderPath, fileName))))
						await this.adapter.mkdir(normalizePath(Path.join(trashFolderPath, fileName)));
					// await this.app.fileManager.renameFile(tfile, newFilePath);
					await this.adapter.rename(normalizePath(file), normalizePath(Path.join(trashFolderPath, fileName, Path.basename(file))));
					// await this.adapter.remove(file);
				}
			}
			// 删完文件再list一遍附件文件夹，如果文件夹内文件删空了，则可以删除整个文件夹
			attachmentFiles = await this.adapter.list(attachmentFolderPath);
			if (attachmentFiles.files.length === 0 && attachmentFiles.folders.length === 0) {
				await this.adapter.rmdir(attachmentFolderPath, true);
			} else {
				console.log(`attachment folder keep exists. there might be other notes assets.`);
				new Notice(`attachment folder keep exists. there might be other notes assets.`);
			}
		}
	}
}

class CustomAttachmentLocationSettingTab extends PluginSettingTab {
    plugin: CustomAttachmentLocation;

    constructor(app: App, plugin: CustomAttachmentLocation) {
        super(app, plugin);
        this.plugin = plugin;
    }

    display(): void {
        let { containerEl } = this;

        containerEl.empty();

        containerEl.createEl('h2', { text: 'Custom Attachment Location' });

        let el = new Setting(containerEl)
            .setName('Location for New Attachments')
            .setDesc('Start with "./" to use relative path. Available variables: ${filename}.(NOTE: DO NOT start with "/" or end with "/". )')
            .addText(text => text
                .setPlaceholder('./assets/${filename}')
                .setValue(this.plugin.settings.attachmentFolderPath)
                .onChange(async (value: string) => {
                    console.log('attachmentFolder: ' + value);
                    value = normalizePath(value);
                    console.log('normalized attachmentFolder: ' + value);

                    this.plugin.settings.attachmentFolderPath = value;
                    if (value.startsWith('./'))
                        this.plugin.useRelativePath = true;
                    else
                        this.plugin.useRelativePath = false;
                    await this.plugin.saveSettings();
                }));
        el.controlEl.addEventListener('change', (() => { this.display(); }));


        new Setting(containerEl)
            .setName('Pasted Image Name')
            .setDesc('Available variables: ${filename}, ${date}.')
            .addText(text => text
                .setPlaceholder('image-${date}')
                .setValue(this.plugin.settings.pastedImageFileName)
                .onChange(async (value: string) => {
                    console.log('pastedImageFileName: ' + value);
                    this.plugin.settings.pastedImageFileName = value;
                    await this.plugin.saveSettings();
                }));

        new Setting(containerEl)
            .setName('Date Format')
            .setDesc('YYYYMMDDHHmmss')
            .addMomentFormat(text => text
                .setDefaultFormat('YYYYMMDDHHmmss')
                .setValue(this.plugin.settings.dateTimeFormat)
                .onChange(async (value: string) => {
                    console.log('dateTimeFormat: ' + value);
                    this.plugin.settings.dateTimeFormat = value || 'YYYYMMDDHHmmss';
                    await this.plugin.saveSettings();
                }));

		new Setting(containerEl)
			.setName('Set Attachment Extension')
			.setDesc(`Set attachment extension considered image type, split by |`)
			.addText(text => text
				.setPlaceholder('.jpeg|.jpg|.png|.gif|.bmp|.tiff|.tif|.svgz|.svg|.jfif|.webp|.ico|.xbm')
				.setValue(this.plugin.settings.attachmentExts)
				.onChange(async (value) => {
						this.plugin.settings.attachmentExts = value;
						await this.plugin.saveSettings();
					}
				));

		new Setting(containerEl)
			.setName('Set Attachment Section')
			.setDesc(`This option set the section part(shown as #...) for the image.`)
			.addText(text => text
				.setPlaceholder('#inline')
				.setValue(this.plugin.settings.attachmentSection)
				.onChange(async (value) => {
						this.plugin.settings.attachmentSection = value;
						await this.plugin.saveSettings();
					}
				));

		new Setting(containerEl)
			.setName('Set Attachment Alias')
			.setDesc(`This option set the alias part(shown as |...) for the image.`)
			.addText(text => text
				.setPlaceholder('图示一|300')
				.setValue(this.plugin.settings.attachmentAlias)
				.onChange(async (value) => {
						this.plugin.settings.attachmentAlias = value;
						await this.plugin.saveSettings();
					}
				));
        new Setting(containerEl)
            .setName('Automatically rename attachment folder')
            .setDesc('When renaming md files, automatically rename attachment folder if folder name contains "${filename}".')
            .addToggle(toggle => toggle
                .setValue(this.plugin.settings.autoRenameFolder)
                .onChange(async (value: boolean) => {
                    this.plugin.settings.autoRenameFolder = value;
                    this.display();
                    await this.plugin.saveSettings();
                }));

		new Setting(containerEl)
			.setName('Vault Start Location')
			.setDesc('the root location for vault level process.')
			.addText(text => text
				.setPlaceholder("e.g. 0_Workshop/working/obsidian-selection-replace")
				.setValue(this.plugin.settings.vaultStartLocation)
				.onChange(async (value) => {
						this.plugin.settings.vaultStartLocation = value;
						await this.plugin.saveSettings();
					}
				))
			.addExtraButton(cb => {
			cb.setIcon("play-circle")
				.setTooltip("Execute")
				.onClick(() => {
					this.plugin.collectVaultNotesAttachments(this.plugin.settings.vaultStartLocation)
				});
		});


        if (this.plugin.settings.autoRenameFolder)
            new Setting(containerEl)
                .setName('Automatically rename attachment files')
                .setDesc('When renaming md files, automatically rename attachment files if file name contains "${filename}".')
                .addToggle(toggle => toggle
                    .setValue(this.plugin.settings.autoRenameFiles)
                    .onChange(async (value: boolean) => {
                        this.plugin.settings.autoRenameFiles = value;
                        await this.plugin.saveSettings();
                    }));
    }
}
